﻿using System;
using System.Drawing;
using System.Collections.Generic;
using Noris.Schedule.Support.Core;
using Noris.Schedule.Support.Data;
using System.Data.SqlTypes;
using Noris.Schedule.Support.Green;

namespace Noris.Schedule.Support.Services
{
    #region Interface IConfiguredText : dovoluje čemukoli pracovat s konfigurací textů pomocí výrazů (vzorec pro tvorbu textu)
    /// <summary>
    /// IConfiguredText : dovoluje čemukoli pracovat s konfigurací textů pomocí výrazů (vzorec pro tvorbu textu)
    /// </summary>
    public interface IConfiguredText
    {
        /// <summary>
        /// Globální identifikátor záznamu = tohoto elementu
        /// </summary>
        GID GId { get; }
        /// <summary>
        /// Globální identifikátor rodičovského záznamu = řádek, v kterém se element nachází.
        /// Pokud je toto čas operace, pak parentem je operace.
        /// Pokud tento element je čas stavu kapacit, jde o kapacitní jednotku.
        /// Pokud tento element představuje část práce z konkrétní operace na konkrétní kapacitě, pak bude zobrazen na dané kapacitě.
        /// Jde o aktuální údaj, který může obsahovat editovanou hodnotu.
        /// </summary>
        GID RowGId { get; set; }
        /// <summary>
        /// Objekt zde deklaruje, že umožňuje konfigurovatelný tooltip
        /// </summary>
        bool EnabledToolTipTextConfigure { get; }
        /// <summary>
        /// Objekt zde deklaruje, že umožňuje konfigurovatelný text přímo v těle prvku
        /// </summary>
        bool EnabledCaptionTextConfigure { get; }
        /// <summary>
        /// Číslo třídy, nad kterou je postaven strom tříd pro tento výraz.
        /// </summary>
        int RootClassNumber { get; }
        /// <summary>
        /// Zde prvek obsahuje stringový klíč, který vede k záznamu konfigurace.
        /// Klíč typicky obsahuje číslo třídy, a druh prvku v této třídě.
        /// Odpovídá hodnotě lcs.scheduler_config.klic.
        /// Pokud prvek vrátí prázdný string, pak pro něj konfigurace textů nebude fungovat.
        /// </summary>
        string ConfigurationKey { get; }
    }
    #endregion
    #region Interface IClassTreeExtender : dovoluje komukoliv (typicky datovému zdroji) rozšiřovat standardní strom tříd Greenu o vlastní třídy, atributy a vztahy.
    /// <summary>
    /// Interface, který dovoluje komukoliv (typicky datovému zdroji) rozšiřovat standardní strom tříd Greenu o vlastní třídy, atributy a vztahy.
    /// </summary>
    public interface IClassTreeExtender
    {
        /// <summary>
        /// Datový zdroj má šanci nadeklarovat rozšířené atributy pro danou třídu.
        /// Jde o atributy, které běžně nejsou v Green atributech, ale datový zdroj je má k dispozici a chce je nabídnout do stromu tříd.
        /// V argumentu je uvedeno číslo třídy, pro kterou se nyní do stromu tříd načítají data.
        /// Tato metoda může do argumentu do args.Attributes přidat další položky, které budou popisovat další dostupné atributy v dané třídě.
        /// Následně by bylo vhodné, aby je při evaluaci dokázal vlastník rozpoznat a vyhodnotit (v metodě IEvaluationDataSource.TryGetValue()).
        /// </summary>
        /// <param name="args"></param>
        void GetExtendedAttributes(ClassTreeExtenderGetAttributesArgs args);
        /// <summary>
        /// Datový zdroj má šanci nadeklarovat rozšířené vztahy pro danou třídu.
        /// Jde o vztahy, které běžně nejsou v Green vztazích, ale datový zdroj je má k dispozici a chce je nabídnout do stromu tříd.
        /// V argumentu je uvedeno číslo třídy, pro kterou se nyní do stromu tříd načítají data.
        /// Tato metoda může do argumentu do args.Relations přidat další položky, které budou popisovat další dostupné vztahy v dané třídě.
        /// Následně by bylo vhodné, aby je při evaluaci dokázal vlastník rozpoznat a vyhodnotit (v metodě IEvaluationDataSource.TryGetDataRelated()).
        /// </summary>
        /// <param name="args"></param>
        void GetExtendedRelations(ClassTreeExtenderGetRelationsArgs args);
    }
    /// <summary>
    /// Data pro metodu IClassTreeExtender.GetExtendedAttributes()
    /// </summary>
    public class ClassTreeExtenderGetAttributesArgs : ClassTreeExtenderArgs
    {
        public ClassTreeExtenderGetAttributesArgs(int classNumber)
            : base(classNumber)
        {
            this.HideGreenAttributesGroup = new List<AttributeGroupType>();
            this.HideGreenAttributes = new List<ClassExtendAttribute>();
            this.ExtendedAttributes = new List<ClassExtendAttribute>();
        }
        /// <summary>
        /// Extender může definovat, že se mají skrýt úplně všechny standardní položky.
        /// </summary>
        public bool HideGreenAttributesAll { get; set; }
        /// <summary>
        /// Extender může vyjmenovat typy atributů, které se mají skrýt.
        /// </summary>
        public List<AttributeGroupType> HideGreenAttributesGroup { get; set; }
        /// <summary>
        /// Extender může vyjmenovat určité konkrétní atributy, které se mají skrýt.
        /// </summary>
        public List<ClassExtendAttribute> HideGreenAttributes { get; set; }
        /// <summary>
        /// Seznam externích atributů. Extender může přidat atributy, ale musí si je následně řešit.
        /// </summary>
        public List<ClassExtendAttribute> ExtendedAttributes { get; private set; }
    }
    /// <summary>
    /// Data pro metodu IClassTreeExtender.GetExtendedRelation()
    /// </summary>
    public class ClassTreeExtenderGetRelationsArgs : ClassTreeExtenderArgs
    {
        public ClassTreeExtenderGetRelationsArgs(int classNumber, bool staticRelationToLeftEnabled)
            : base(classNumber)
        {
            this.StaticRelationToLeftEnabled = staticRelationToLeftEnabled;
            this.HideGreenRelations = new List<int>();
            this.ExtendedRelations = new List<ClassExtendRelation>();
        }
        /// <summary>
        /// Příznak, že strom tříd může zobrazovat statické vztahy doleva (zpětné)
        /// </summary>
        public bool StaticRelationToLeftEnabled { get; private set; }
        /// <summary>
        /// Extender může definovat, že se mají skrýt úplně všechny standardní vztahy.
        /// </summary>
        public bool HideGreenRelationsAll { get; set; }
        /// <summary>
        /// Extender může vyjmenovat určité konkrétní vztahy, které se mají skrýt.
        /// </summary>
        public List<int> HideGreenRelations { get; set; }
        /// <summary>
        /// Seznam externích vztahů. Extender může přidat vztahy, ale musí si je následně řešit.
        /// Klíčem záznamu je číslo vztahu, ukládá se do definice proměnné jako běžné číslo vztahu.
        /// Z tohoto důvodu je bezpodmínečně nutné, aby číslo vztahu bylo unikátní vzhledem k číslům vztahů použitých v Greenu.
        /// Při pohledu do "CASE - Autoři tříd" doporučuji využít rezervovanou oblast čísel: 50000 - 89999 pro rozšířené vztahy plánovací tabule.
        /// Tyto vztahy se nikdy nedostanou do databáze Green kromě jediného místa (definice textu proměnné, kde bude uveden v definici proměnné, typicky: «{C1817:R65536:C1364:Master.nazev_subjektu}» )
        /// My tím data Green nijak neohrozíme.
        /// Pokud by v budoucnu teoreticky došlo k použití čísel z rezervované řady pro reálné vztahy, došlo by ke konfliktu pouze tehdy, když by daný reálný vztah byl použit na naší třídě. Prakticky je to vyloučeno.
        /// Navrhuji rozdělit si čísla vztahů takto:
        /// ASOL   (standard): řada čísel 65536 ÷ 69631 (0x10000 ÷ 0x10FFF)
        /// Gatema (extender): řada čísel 70000 ÷ 70999
        /// </summary>
        public List<ClassExtendRelation> ExtendedRelations { get; private set; }
    }
    /// <summary>
    /// Základní data pro metody interface IClassTreeExtender.
    /// </summary>
    public class ClassTreeExtenderArgs
    {
        public ClassTreeExtenderArgs(int classNumber)
        {
            this.ClassNumber = classNumber;
        }
        /// <summary>
        /// Číslo třídy
        /// </summary>
        public int ClassNumber { get; private set; }
        /// <summary>
        /// Data o třídě z repozitory
        /// </summary>
        public ClassInfo ClassInfo
        {
            get
            {
                ClassInfo classInfo;
                if (this.ClassNumber != 0 && Repository.Classes.TryGetValue(this.ClassNumber, out classInfo))
                    return classInfo;
                return null;
            }
        }
    }
    /// <summary>
    /// ClassAttribute : základní obecný popis jednoho atributu.
    /// Atribut lze použít i jako Key v Hashtable, nebo lze použít operátor == a !=. Vyhodnocuje se shoda GroupType a Column. Ignoruje se název a tooltip.
    /// </summary>
    public class ClassExtendAttribute
    {
        #region Konstrukce
        public ClassExtendAttribute(string name, string column)
        {
            this.GroupType = AttributeGroupType.Extended;
            this.Name = name;
            this.Column = column;
            this.ToolTipText = null;
        }
        public ClassExtendAttribute(string name, string column, string toolTipText)
        {
            this.GroupType = AttributeGroupType.Extended;
            this.Name = name;
            this.Column = column;
            this.ToolTipText = toolTipText;
        }
        public ClassExtendAttribute(AttributeGroupType groupType, string name, string column, string toolTipText)
        {
            this.GroupType = groupType;
            this.Name = name;
            this.Column = column;
            this.ToolTipText = toolTipText;
        }
        public override int GetHashCode()
        {
            return this.GroupType.GetHashCode() ^ (this.Column == null ? 0 : this.Column.GetHashCode());
        }
        public override bool Equals(object obj)
        {
            ClassExtendAttribute other = obj as ClassExtendAttribute;
            if (other == null) return false;
            return (this.GroupType == other.GroupType && String.Equals(this.Column, other.Column, StringComparison.InvariantCulture));
        }
        public override string ToString()
        {
            return this.GroupType.ToString() + "." + this.Name;
        }
        public static bool operator ==(ClassExtendAttribute a, ClassExtendAttribute b)
        {
            bool aNull = (((object)a) == null);
            bool bNull = (((object)a) == null);
            if (aNull && bNull) return true;
            if (aNull || bNull) return false;
            return a.Equals(b);
        }
        public static bool operator !=(ClassExtendAttribute a, ClassExtendAttribute b)
        {
            bool aNull = (((object)a) == null);
            bool bNull = (((object)a) == null);
            if (aNull && bNull) return false;
            if (aNull || bNull) return true;
            return !a.Equals(b);
        }
        #endregion
        #region Standardní property
        /// <summary>
        /// Typ položky, odkud pochází.
        /// </summary>
        public AttributeGroupType GroupType { get; private set; }
        /// <summary>
        /// Název skupiny ve vizuálním seznamu položek.
        /// </summary>
        public string GroupName { get { return ConvertGroupTypeToName(this.GroupType); } }
        /// <summary>
        /// Název 
        /// </summary>
        public string Name { get; private set; }
        /// <summary>
        /// Sloupec
        /// </summary>
        public string Column { get; private set; }
        /// <summary>
        /// Informace do Tooltipu k této položce
        /// </summary>
        public string ToolTipText { get; private set; }
        /// <summary>
        /// Obsahuje krátké jméno tohoto atributu = pouze "typ tabulky.název sloupce".
        /// Neobsahuje FullTreePath ani vnejší závorky !!!
        /// Název tabulky je nahrazen textem: Subjekt (použije se pouze pro atributy na netypovém vztahu), Master, MasterUda, Entries, EntriesUda.
        /// Ostatní atributy (nepocházející z repozitory) mají uveden text "Extended".
        /// Atribut typu RecordNumber obsahuje "Master.cislo_subjektu" / "Master.cislo_nonsubjektu".
        /// Atribut typu Expression neobsahuje nic.
        /// </summary>
        public string ShortVariableName
        {
            get
            {
                string dotColumn = "." + this.Column;
                switch (this.GroupType)
                {
                    case AttributeGroupType.Subjekt:
                        return Noris.Schedule.Support.Expressions.VariableItem.TABLENAME_SUBJEKT + dotColumn;
                    case AttributeGroupType.Master:
                        return Noris.Schedule.Support.Expressions.VariableItem.TABLENAME_MASTER + dotColumn;
                    case AttributeGroupType.MasterUda:
                        return Noris.Schedule.Support.Expressions.VariableItem.TABLENAME_MASTERUDA + dotColumn;
                    case AttributeGroupType.Entries:
                        return Noris.Schedule.Support.Expressions.VariableItem.TABLENAME_ENTRIES + dotColumn;
                    case AttributeGroupType.EntriesUda:
                        return Noris.Schedule.Support.Expressions.VariableItem.TABLENAME_ENTRIESUDA + dotColumn;
                    case AttributeGroupType.Extended:
                        return Noris.Schedule.Support.Expressions.VariableItem.TABLENAME_EXTENDED + dotColumn;
                    case AttributeGroupType.System:
                        return Noris.Schedule.Support.Expressions.VariableItem.TABLENAME_SYSTEM + dotColumn;
                    case AttributeGroupType.RecordNumber:
                        return Noris.Schedule.Support.Expressions.VariableItem.TABLENAME_MASTER + dotColumn;
                    case AttributeGroupType.Expression:
                        return "";
                }
                return this.GroupType.ToString() + "." + this.Column;
            }
        }
        #endregion
        #region Konverze AttributeGroupType na název List.Group
        /// <summary>
        /// Převede druh položky (detailnější) na název skupiny v ListView (některé typy jsou sloučeny do jedné vizuální skupiny).
        /// </summary>
        /// <param name="groupType"></param>
        /// <returns></returns>
        public static string ConvertGroupTypeToName(AttributeGroupType groupType)
        {
            switch (groupType)
            {
                case AttributeGroupType.Subjekt: return ATTRIBUTEGROUP_MASTER;
                case AttributeGroupType.Master: return ATTRIBUTEGROUP_MASTER;
                case AttributeGroupType.MasterUda: return ATTRIBUTEGROUP_MASTER;
                case AttributeGroupType.Entries: return ATTRIBUTEGROUP_ENTRIES;
                case AttributeGroupType.EntriesUda: return ATTRIBUTEGROUP_ENTRIES;
                case AttributeGroupType.Extended: return ATTRIBUTEGROUP_EXTENDED;
                case AttributeGroupType.System: return ATTRIBUTEGROUP_SYSTEM;
                case AttributeGroupType.RecordNumber: return ATTRIBUTEGROUP_SYSTEM;
                case AttributeGroupType.Expression: return ATTRIBUTEGROUP_SYSTEM;
            }
            return null;
        }
        #endregion
        #region Konstanty
        public const string ATTRIBUTEGROUP_MASTER = "Master";
        public const string ATTRIBUTEGROUP_ENTRIES = "Entries";
        public const string ATTRIBUTEGROUP_EXTENDED = "Extended";
        public const string ATTRIBUTEGROUP_SYSTEM = "System";
        #endregion
    }
    /// <summary>
    /// Skupina, do které patří atribut v seznamu atributů
    /// </summary>
    public enum AttributeGroupType
    {
        None,
        Subjekt,
        Master,
        MasterUda,
        Entries,
        EntriesUda,
        Extended,
        System,
        RecordNumber,
        Expression
    }
    /// <summary>
    /// ClassExtendRelation : základní obecný popis jednoho vztahu.
    /// ClassExtendRelation lze použít i jako Key v Hashtable, nebo lze použít operátor == a !=. Vyhodnocuje se shoda Number. Ignoruje se název a tooltip.
    /// </summary>
    public class ClassExtendRelation
    {
        #region Konstrukce
        /// <summary>
        /// Konstruktor pro extended vztah
        /// </summary>
        /// <param name="number">Číslo vztahu. Klíč, ukládá se do definice proměnné mezi ostatní (standardní) vztahy.
        /// Z tohoto důvodu je bezpodmínečně nutné, aby číslo vztahu bylo unikátní vzhledem k číslům vztahů použitých v Greenu.
        /// Při pohledu do "CASE - Autoři tříd" doporučuji využít rezervovanou oblast čísel: 50000 - 89999 pro rozšířené vztahy plánovací tabule.
        /// Tyto vztahy se nikdy nedostanou do databáze Green kromě jediného místa (definice textu proměnné, kde bude uveden v definici proměnné, typicky: «{C1817:R65536:C1364:Master.nazev_subjektu}» )
        /// My tím data Green nijak neohrozíme.
        /// Pokud by v budoucnu teoreticky došlo k použití čísel z rezervované řady pro reálné vztahy, došlo by ke konfliktu pouze tehdy, když by daný reálný vztah byl použit na naší třídě. Prakticky je to vyloučeno.
        /// Navrhuji rozdělit si čísla vztahů takto:
        /// ASOL   (standard): řada čísel 65536 ÷ 69631 (0x10000 ÷ 0x10FFF)
        /// Gatema (extender): řada čísel 70000 ÷ 70999
        /// </param>
        /// <param name="targetClass">Číslo podřízené třídy v tomto vztahu. V extended vztahu nesmí být netypový vztah (na více než jednu třídu).</param>
        /// <param name="name">Uživatelský název vztahu</param>
        public ClassExtendRelation(int number, string name, int targetClass)
        {
            this.RelationMark = 'E';
            this.Number = number;
            this.Name = name;
            this.TargetClass = targetClass;
            this.ToolTipText = "Vztah " + number.ToString() + ": " + name + Environment.NewLine + "       Rozšířený vztah";
        }
        /// <summary>
        /// Konstruktor pro extended vztah
        /// </summary>
        /// <param name="relationType">Typ vztahu, značka do FullTreePath. Typicky (default) je zde 'E' = Extended. Lze zadat jiný znak, využívá se 'F' = Folder.</param>
        /// <param name="number">Číslo vztahu. Klíč, ukládá se do definice proměnné mezi ostatní (standardní) vztahy.
        /// Z tohoto důvodu je bezpodmínečně nutné, aby číslo vztahu bylo unikátní vzhledem k číslům vztahů použitých v Greenu.
        /// Při pohledu do "CASE - Autoři tříd" doporučuji využít rezervovanou oblast čísel: 50000 - 89999 pro rozšířené vztahy plánovací tabule.
        /// Tyto vztahy se nikdy nedostanou do databáze Green kromě jediného místa (definice textu proměnné, kde bude uveden v definici proměnné, typicky: «{C1817:R65536:C1364:Master.nazev_subjektu}» )
        /// My tím data Green nijak neohrozíme.
        /// Pokud by v budoucnu teoreticky došlo k použití čísel z rezervované řady pro reálné vztahy, došlo by ke konfliktu pouze tehdy, když by daný reálný vztah byl použit na naší třídě. Prakticky je to vyloučeno.
        /// Navrhuji rozdělit si čísla vztahů takto:
        /// ASOL   (standard): řada čísel 65536 ÷ 69631 (0x10000 ÷ 0x10FFF)
        /// Gatema (extender): řada čísel 70000 ÷ 70999
        /// </param>
        /// <param name="name">Uživatelský název vztahu</param>
        /// <param name="targetClass">Číslo podřízené třídy v tomto vztahu. V extended vztahu nesmí být netypový vztah (na více než jednu třídu).</param>
        public ClassExtendRelation(char relationType, int number, string name, int targetClass)
        {
            this.RelationMark = relationType;
            this.Number = number;
            this.Name = name;
            this.TargetClass = targetClass;
            this.ToolTipText = "Vztah " + number.ToString() + ": " + name + Environment.NewLine + "       Rozšířený vztah";
        }
        /// <summary>
        /// Konstruktor pro extended vztah
        /// </summary>
        /// <param name="number">Číslo vztahu. Klíč, ukládá se do definice proměnné mezi ostatní (standardní) vztahy.
        /// Z tohoto důvodu je bezpodmínečně nutné, aby číslo vztahu bylo unikátní vzhledem k číslům vztahů použitých v Greenu.
        /// Při pohledu do "CASE - Autoři tříd" doporučuji využít rezervovanou oblast čísel: 50000 - 89999 pro rozšířené vztahy plánovací tabule.
        /// Tyto vztahy se nikdy nedostanou do databáze Green kromě jediného místa (definice textu proměnné, kde bude uveden v definici proměnné, typicky: «{C1817:R65536:C1364:Master.nazev_subjektu}» )
        /// My tím data Green nijak neohrozíme.
        /// Pokud by v budoucnu teoreticky došlo k použití čísel z rezervované řady pro reálné vztahy, došlo by ke konfliktu pouze tehdy, když by daný reálný vztah byl použit na naší třídě. Prakticky je to vyloučeno.
        /// Navrhuji rozdělit si čísla vztahů takto:
        /// ASOL   (standard): řada čísel 65536 ÷ 69631 (0x10000 ÷ 0x10FFF)
        /// Gatema (extender): řada čísel 70000 ÷ 70999
        /// </param>
        /// <param name="name">Uživatelský název vztahu</param>
        /// <param name="targetClass">Číslo podřízené třídy v tomto vztahu. V extended vztahu nesmí být netypový vztah (na více než jednu třídu).</param>
        /// <param name="toolTipText">Text tooltipu.
        /// Doporučuje se vzhled: "Vztah 1234: název {newline} {7 mezer} Rozšířený vztah" (což je default, to není třeba ručně vytvářet).</param>
        public ClassExtendRelation(int number, string name, int targetClass, string toolTipText)
        {
            this.RelationMark = 'E';
            this.Number = number;
            this.Name = name;
            this.TargetClass = targetClass;
            this.ToolTipText = toolTipText;
        }
        /// <summary>
        /// Konstruktor pro extended vztah
        /// </summary>
        /// <param name="relationType">Typ vztahu, značka do FullTreePath. Typicky (default) je zde 'E' = Extended. Lze zadat jiný znak, využívá se 'F' = Folder.</param>
        /// <param name="number">Číslo vztahu. Klíč, ukládá se do definice proměnné mezi ostatní (standardní) vztahy.
        /// Z tohoto důvodu je bezpodmínečně nutné, aby číslo vztahu bylo unikátní vzhledem k číslům vztahů použitých v Greenu.
        /// Při pohledu do "CASE - Autoři tříd" doporučuji využít rezervovanou oblast čísel: 50000 - 89999 pro rozšířené vztahy plánovací tabule.
        /// Tyto vztahy se nikdy nedostanou do databáze Green kromě jediného místa (definice textu proměnné, kde bude uveden v definici proměnné, typicky: «{C1817:R65536:C1364:Master.nazev_subjektu}» )
        /// My tím data Green nijak neohrozíme.
        /// Pokud by v budoucnu teoreticky došlo k použití čísel z rezervované řady pro reálné vztahy, došlo by ke konfliktu pouze tehdy, když by daný reálný vztah byl použit na naší třídě. Prakticky je to vyloučeno.
        /// Navrhuji rozdělit si čísla vztahů takto:
        /// ASOL   (standard): řada čísel 65536 ÷ 69631 (0x10000 ÷ 0x10FFF)
        /// Gatema (extender): řada čísel 70000 ÷ 70999
        /// </param>
        /// <param name="targetClass">Číslo podřízené třídy v tomto vztahu. V extended vztahu nesmí být netypový vztah (na více než jednu třídu).</param>
        /// <param name="name">Uživatelský název vztahu</param>
        /// <param name="toolTipText">Text tooltipu.
        /// Doporučuje se vzhled: "Vztah 1234: název {newline} {7 mezer} Rozšířený vztah" (což je default, to není třeba ručně vytvářet).</param>
        public ClassExtendRelation(char relationType, int number, string name, int targetClass, string toolTipText)
        {
            this.RelationMark = relationType;
            this.Number = number;
            this.Name = name;
            this.TargetClass = targetClass;
            this.ToolTipText = toolTipText;
        }
        public override int GetHashCode()
        {
            return this.Number.GetHashCode();
        }
        public override bool Equals(object obj)
        {
            ClassExtendRelation other = obj as ClassExtendRelation;
            if (other == null) return false;
            return (this.Number == other.Number);
        }
        public override string ToString()
        {
            return this.Number.ToString() + ": " + this.Name;
        }
        public static bool operator ==(ClassExtendRelation a, ClassExtendRelation b)
        {
            bool aNull = (((object)a) == null);
            bool bNull = (((object)a) == null);
            if (aNull && bNull) return true;
            if (aNull || bNull) return false;
            return a.Equals(b);
        }
        public static bool operator !=(ClassExtendRelation a, ClassExtendRelation b)
        {
            bool aNull = (((object)a) == null);
            bool bNull = (((object)a) == null);
            if (aNull && bNull) return false;
            if (aNull || bNull) return true;
            return !a.Equals(b);
        }
        #endregion
        #region Standardní property
        /// <summary>
        /// Typ vztahu, značka do FullTreePath.
        /// Typicky je zde 'E' = Extended.
        /// Lze zadat (v konstruktoru parametrem na první pozici) jiný znak, využívá se 'F' = Folder.
        /// </summary>
        public char RelationMark { get; private set; }
        /// <summary>
        /// Číslo vztahu. Klíč, ukládá se do definice proměnné mezi ostatní (standardní) vztahy.
        /// Z tohoto důvodu je bezpodmínečně nutné, aby číslo vztahu bylo unikátní vzhledem k číslům vztahů použitých v Greenu.
        /// Při pohledu do "CASE - Autoři tříd" doporučuji využít rezervovanou oblast čísel: 50000 - 89999 pro rozšířené vztahy plánovací tabule.
        /// Tyto vztahy se nikdy nedostanou do databáze Green kromě jediného místa (definice textu proměnné, kde bude uveden v definici proměnné, typicky: «{C1817:R65536:C1364:Master.nazev_subjektu}» )
        /// My tím data Green nijak neohrozíme.
        /// Pokud by v budoucnu teoreticky došlo k použití čísel z rezervované řady pro reálné vztahy, došlo by ke konfliktu pouze tehdy, když by daný reálný vztah byl použit na naší třídě. Prakticky je to vyloučeno.
        /// Navrhuji rozdělit si čísla vztahů takto:
        /// ASOL   (standard): řada čísel 65536 ÷ 69631 (0x10000 ÷ 0x10FFF)
        /// Gatema (extender): řada čísel 70000 ÷ 70999
        /// </summary>
        public int Number { get; private set; }
        /// <summary>
        /// Název vztahu
        /// </summary>
        public string Name { get; private set; }
        /// <summary>
        /// Číslo podřízené třídy v tomto vztahu. V extended vztahu nesmí být netypový vztah (na více než jednu třídu).
        /// Target třída musí být standardní Norisovská !!!
        /// Zatím nepovolujeme přidávat Extended třídy (i když by to dozajista bylo fajn).
        /// </summary>
        public int TargetClass { get; private set; }
        /// <summary>
        /// Informace do Tooltipu k této položce
        /// </summary>
        public string ToolTipText { get; private set; }
        #endregion
    }
    #endregion
    #region Interface IEvaluationDataSource : deklarace vlastností objektu, který umožňuje vyhodnocovat textové proměnné (pro konfigurované texty: IConfiguredText)
    /// <summary>
    /// Interface IEvaluationDataSource : deklarace vlastností objektu, který umožňuje vyhodnocovat textové proměnné (pro konfigurované texty: IConfiguredText)
    /// </summary>
    public interface IEvaluationDataSource
    {
        /// <summary>
        /// Datový zdroj se pokusí najít datový objekt podle jeho klíče (číslo třídy + číslo záznamu).
        /// Objekt ukládá do args.ResultData a vrací true.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        bool TryGetDataObject(EvaluationDataSourceGetObjectArgs args);
        /// <summary>
        /// Datový zdroj se pokusí najít datový objekt navázaný k danému záznamu v daném vztahu.
        /// Pokud datový zdroj nic nenajde, pak se jádro pokusí určit klíč cílového záznamu samo (najde údaj ve výchozím záznamu, který odpovídá vztahu, a vyhodnotí jej)
        /// a následně zavolá metodu this.TryGetDataClass() pro takto určený záznam.
        /// Objekt ukládá do args.ResultData a vrací true.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        bool TryGetDataRelated(EvaluationDataSourceGetRelatedObjectArgs args);
        /// <summary>
        /// Datový zdroj se pokusí najít hodnotu atributu v datovém objektu dle specifikce.
        /// Objekt ukládá do args.ResultValue a vrací true.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        bool TryGetValue(EvaluationDataSourceGetValueArgs args);
    }
    /// <summary>
    /// Třída s daty pro vyhledání datového objektu (buď root, nebo jakýkoli jiný objekt určený číslem záznamu GID)
    /// </summary>
    public class EvaluationDataSourceGetObjectArgs : EvaluationDataSourceArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="variable">Celá proměnná (všechny položky)</param>
        /// <param name="index">Index aktuální položky proměnné</param>
        /// <param name="configuredText">Data elementu</param>
        public EvaluationDataSourceGetObjectArgs(Expressions.Variable variable, int index, IConfiguredText configuredText, GID searchGId)
            : base(variable, index, configuredText)
        {
            this.SearchGId = searchGId;
        }
        /// <summary>
        /// GID záznamu, který budeme hledat
        /// </summary>
        public GID SearchGId { get; private set; }
        /// <summary>
        /// Sem bude umístěn nalezený objekt s daty.
        /// Pokud zde bude null, značí to že metoda nedokázala určit hledaný objekt (hledání bude pokračovat v SQL databázi).
        /// Hodnotu je internal: neumisťuje ji datový zdroj přímo, ale pomocí metody FillRelationRecord(), 
        /// to aby se dodržela obdobná konvence společná s ostatními typy argumentu.
        /// </summary>
        internal object ResultData { get; set; }
        /// <summary>
        /// Speciální metoda pro uložení předané hodnoty do ResultData, a pro vrácení true.
        /// Zní to divně, ale hodí se to - v metodě: IEvaluationDataSource.TryGetDataObject(args), která má vracet bool.
        /// Běžná konstrukce kódu vede k tomu, že v switch (args.ClassNumber) se rozbočuje dle čísla hledané třídy,
        /// a pak se hledá konkrétní objekt. Ten se má uložit do args.ResultValue a má se vrátit true, pokud se objekt skutečně našel.
        /// Zjednodušení kódu lze dosáhnout tak, že se použije konstrukce:
        /// case 1234: return args.FillResultData(FindObject(123456));       // Do args.ResultData uloží nalezená data, a vrátí true / false podle toho, zda se našel objekt != null.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool FillResultData(object data)
        {
            this.ResultData = data;
            this.CurrentVariableItem.InMemoryDataObject = data;
            this.CurrentVariableItem.InMemoryFound = (data != null);
            if (data is IRecord)
                this.CurrentVariableItem.InMemoryRecordNumberMaster = ((IRecord)data).RecordNumber;
            return (data != null);
        }
        /// <summary>
        /// Speciální metoda pro uložení předané hodnoty do ResultData, a pro vrácení true.
        /// Zní to divně, ale hodí se to - v metodě: IEvaluationDataSource.TryGetDataObject(args), která má vracet bool.
        /// Běžná konstrukce kódu vede k tomu, že v switch (args.ClassNumber) se rozbočuje dle čísla hledané třídy,
        /// a pak se hledá konkrétní objekt. Ten se má uložit do args.ResultValue a má se vrátit true, pokud se objekt skutečně našel.
        /// Zjednodušení kódu lze dosáhnout tak, že se použije konstrukce:
        /// case 1234: return args.FillResultData(FindObject(123456));       // Do args.ResultData uloží nalezená data, a vrátí true / false podle toho, zda se našel objekt != null.
        /// </summary>
        /// <param name="relatedRecordNumber">Číslo záznamu</param>
        /// <returns></returns>
        public bool FillResultData(Int32? relatedRecordNumber)
        {
            if (relatedRecordNumber.HasValue)
                this.CurrentVariableItem.InMemoryRecordNumberMaster = relatedRecordNumber;
            return (relatedRecordNumber.HasValue);
        }
        /// <summary>
        /// Speciální metoda pro uložení předané hodnoty do ResultData, a pro vrácení true.
        /// Zní to divně, ale hodí se to - v metodě: IEvaluationDataSource.TryGetDataObject(args), která má vracet bool.
        /// Běžná konstrukce kódu vede k tomu, že v switch (args.ClassNumber) se rozbočuje dle čísla hledané třídy,
        /// a pak se hledá konkrétní objekt. Ten se má uložit do args.ResultValue a má se vrátit true, pokud se objekt skutečně našel.
        /// Zjednodušení kódu lze dosáhnout tak, že se použije konstrukce:
        /// case 1234: return args.FillResultData(FindObject(123456));       // Do args.ResultData uloží nalezená data, a vrátí true / false podle toho, zda se našel objekt != null.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="relatedRecordNumberMaster">Číslo záznamu</param>
        /// <returns></returns>
        public bool FillResultData(object data, Int32? relatedRecordNumberMaster)
        {
            this.ResultData = data;
            this.CurrentVariableItem.InMemoryDataObject = data;
            this.CurrentVariableItem.InMemoryFound = (data != null);
            if (relatedRecordNumberMaster.HasValue)
                this.CurrentVariableItem.InMemoryRecordNumberMaster = relatedRecordNumberMaster;
            return (data != null);
        }
		/// <summary>
		/// Speciální metoda pro uložení předané hodnoty do ResultData, a pro vrácení true.
		/// Zní to divně, ale hodí se to - v metodě: IEvaluationDataSource.TryGetDataObject(args), která má vracet bool.
		/// Běžná konstrukce kódu vede k tomu, že v switch (args.ClassNumber) se rozbočuje dle čísla hledané třídy,
		/// a pak se hledá konkrétní objekt. Ten se má uložit do args.ResultValue a má se vrátit true, pokud se objekt skutečně našel.
		/// Zjednodušení kódu lze dosáhnout tak, že se použije konstrukce:
		/// case 1234: return args.FillResultData(FindObject(123456));       // Do args.ResultData uloží nalezená data, a vrátí true / false podle toho, zda se našel objekt != null.
		/// </summary>
		/// <param name="data"></param>
		/// <param name="relatedRecordNumberMaster">Číslo záznamu</param>
		/// <returns></returns>
		public bool FillResultData(object data, Int32? relatedRecordNumberMaster, Int32? relatedRecordNumberEntry)
		{
			this.ResultData = data;
			this.CurrentVariableItem.InMemoryDataObject = data;
			this.CurrentVariableItem.InMemoryFound = (data != null);
			if (relatedRecordNumberMaster.HasValue)
			{
				this.CurrentVariableItem.InMemoryRecordNumberMaster = relatedRecordNumberMaster;
				this.CurrentVariableItem.InMemoryRecordNumberEntry = relatedRecordNumberEntry;
			}
			return (data != null);
		}

    }
    /// <summary>
    /// Třída s daty pro vyhledání datového objektu na základě vztahu z předchozí úrovně.
    /// Je zadán objekt OriginData (parent ve vztahu), je zadán vztah, a hledáme navázaný záznam.
    /// </summary>
    public class EvaluationDataSourceGetRelatedObjectArgs : EvaluationDataSourceArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="variable">Celá proměnná (všechny položky)</param>
        /// <param name="index">Index aktuální položky proměnné</param>
        /// <param name="configuredText">Data elementu</param>
        public EvaluationDataSourceGetRelatedObjectArgs(Expressions.Variable variable, int index, IConfiguredText configuredText)
            : base(variable, index, configuredText)
        {
        }
        /// <summary>
        /// <para>
        /// Položka proměnné, která určuje výchozí datový objekt.
        /// </para>
        /// <para>
        /// Mějme strom tříd "C1188 : R9298 : C1151 : Master.datum_vzniku" (Výrobní příkaz - Dílec - Datum vzniku).
        /// Pak OriginVariableItem popisuje data výrobního příkazu (C1188),
        /// dále pak RelationVariableItem popisuje statický vztah 9298 (na dílec)
        /// a konečně CurrentVariableItem předepisuje hledání záznamu třídy 1151: dílec.
        /// </para>
        /// </summary>
        public Expressions.VariableItem OriginVariableItem { get { return this.Variable.Items[this.CurrentVariableIndex - 2]; } }
        /// <summary>
        /// <para>
        /// Položka proměnné, která určuje vztah mezi výchozím datovým objektem a cílovým objektem.
        /// </para>
        /// <para>
        /// Mějme strom tříd "C1188 : R9298 : C1151 : Master.datum_vzniku" (Výrobní příkaz - Dílec - Datum vzniku).
        /// Pak OriginVariableItem popisuje data výrobního příkazu (C1188),
        /// dále pak RelationVariableItem popisuje statický vztah 9298 (na dílec)
        /// a konečně CurrentVariableItem předepisuje hledání záznamu třídy 1151: dílec.
        /// </para>
        /// </summary>
        public Expressions.VariableItem RelationVariableItem { get { return this.Variable.Items[this.CurrentVariableIndex - 1]; } }
        /// <summary>
        /// Obsahuje výchozí objekt s daty. Není null (jinak bychom nebyli zde a s těmito daty).
        /// </summary>
        public object OriginData { get { return this.OriginVariableItem.InMemoryDataObject; } }
        /// <summary>
        /// Číslo třídy výchozího objektu this.OriginData.
        /// Jde o předpokládané číslo třídy, jak je uvedeno ve stromu tříd - nejde o reálné číslo třídy ze záznamu, který je uveden v this.OriginData.
        /// </summary>
        public int OriginClassNumber { get { return this.OriginVariableItem.ClassNumber; } }
        /// <summary>
        /// Číslo vztahu mezi OriginData a ResultData, podle stromu tříd.
        /// </summary>
        public int TargetRelationNumber { get { return this.RelationVariableItem.RelationNumber; } }
		/// <summary>
        /// Typ vztahu mezi OriginData a ResultData, podle definice ze stromu tříd. Zde může být pouze RelationToLeft nebo RelationToRight.
        /// </summary>
        public Expressions.VariableItemType TargetRelationType { get { return this.RelationVariableItem.ItemType; } }
        /// <summary>
        /// Číslo třídy hledaného objektu, podle stromu tříd.
        /// </summary>
        public int TargetClassNumber { get { return this.CurrentVariableItem.ClassNumber; } }
        /// <summary>
        /// Sem bude umístěn nalezený objekt s daty.
        /// Pokud zde bude null, značí to že metoda nedokázala určit hledaný objekt (hledání bude pokračovat v SQL databázi).
        /// Hodnotu je internal: neumisťuje ji datový zdroj přímo, ale pomocí metody FillRelationRecord(), 
        /// to aby se dodržela obdobná konvence společná s ostatními typy argumentu.
        /// </summary>
        internal object ResultData { get; set; }
        /// <summary>
        /// Do aktuálních dat pro vyhledání vztaženého záznamu vyplní číslo záznamu vztaženého objektu.
        /// Použije se tehdy, když při hledání vztaženého objektu (v paměti) známe jeho číslo, ale objekt samotný nemáme v paměti načten.
        /// Typicky při hledání vztaženého záznamu pro statický vztah známe číslo vztaženého subjektu, protože jej máme uložené v objektu this.OriginData v některé property,
        /// ale navázaný objekt v paměti nemáme. 
        /// Uložíme tedy jeho číslo touto metodou, a z metody TryGetDataRelated() vrátíme false (tj. objekt jsme nenašli).
        /// Následně tuto informaci převezme Evaluator, a postaví SELECT tak, že bude začínat vyhledáním právě tohoto záznamu...
        /// </summary>
		/// <param name="relatedRecordNumberMaster">Číslo záznamu (Master = cislo_subjektu / cislo_nonsubjektu) do vztahu</param>
        public bool FillRelationRecord(Int32 relatedRecordNumberMaster)
        {
            Int32? record = (relatedRecordNumberMaster == 0  ? (Int32?)null : (Int32?)relatedRecordNumberMaster);
            return _FillRelationRecord(record, null);
        }
        /// <summary>
        /// Do aktuálních dat pro vyhledání vztaženého záznamu vyplní číslo záznamu vztaženého objektu.
        /// Použije se tehdy, když při hledání vztaženého objektu (v paměti) známe jeho číslo, ale objekt samotný nemáme v paměti načten.
        /// Typicky při hledání vztaženého záznamu pro statický vztah známe číslo vztaženého subjektu, protože jej máme uložené v objektu this.OriginData v některé property,
        /// ale navázaný objekt v paměti nemáme. 
        /// Uložíme tedy jeho číslo touto metodou, a z metody TryGetDataRelated() vrátíme false (tj. objekt jsme nenašli).
        /// Následně tuto informaci převezme Evaluator, a postaví SELECT tak, že bude začínat vyhledáním právě tohoto záznamu...
        /// </summary>
		/// <param name="relatedRecordNumberMaster">Číslo záznamu (Master = cislo_subjektu / cislo_nonsubjektu) do vztahu</param>
		public bool FillRelationRecord(SqlInt32 relatedRecordNumberMaster)
        {
            Int32? record = (relatedRecordNumberMaster.IsNull ? (Int32?)null : (Int32?)relatedRecordNumberMaster.Value);
			return _FillRelationRecord(record, null);
        }
        /// <summary>
        /// Do aktuálních dat pro vyhledání vztaženého záznamu vyplní číslo záznamu vztaženého objektu.
        /// Použije se tehdy, když při hledání vztaženého objektu (v paměti) známe jeho číslo, ale objekt samotný nemáme v paměti načten.
        /// Typicky při hledání vztaženého záznamu pro statický vztah známe číslo vztaženého subjektu, protože jej máme uložené v objektu this.OriginData v některé property,
        /// ale navázaný objekt v paměti nemáme. 
        /// Uložíme tedy jeho číslo touto metodou, a z metody TryGetDataRelated() vrátíme false (tj. objekt jsme nenašli).
        /// Následně tuto informaci převezme Evaluator, a postaví SELECT tak, že bude začínat vyhledáním právě tohoto záznamu...
        /// </summary>
		/// <param name="relatedRecordNumberMaster">Číslo záznamu (Master = cislo_subjektu / cislo_nonsubjektu) do vztahu</param>
		public bool FillRelationRecord(Int32? relatedRecordNumberMaster)
        {
			return _FillRelationRecord(relatedRecordNumberMaster, null);
        }


		/// <summary>
		/// Do aktuálních dat pro vyhledání vztaženého záznamu vyplní číslo záznamu vztaženého objektu.
		/// Použije se tehdy, když při hledání vztaženého objektu (v paměti) známe jeho číslo, ale objekt samotný nemáme v paměti načten.
		/// Typicky při hledání vztaženého záznamu pro statický vztah známe číslo vztaženého subjektu, protože jej máme uložené v objektu this.OriginData v některé property,
		/// ale navázaný objekt v paměti nemáme. 
		/// Uložíme tedy jeho číslo touto metodou, a z metody TryGetDataRelated() vrátíme false (tj. objekt jsme nenašli).
		/// Následně tuto informaci převezme Evaluator, a postaví SELECT tak, že bude začínat vyhledáním právě tohoto záznamu...
		/// </summary>
		/// <param name="relatedRecordNumberMaster">Číslo záznamu (Master = cislo_subjektu / cislo_nonsubjektu) do vztahu</param>
		/// <param name="relatedRecordNumberEntry">Číslo položky záznamu (Entry = cislo_objektu) do vztahu</param>
		public bool FillRelationRecord(Int32 relatedRecordNumberMaster, Int32 relatedRecordNumberEntry)
		{
			Int32? recordMaster = (relatedRecordNumberMaster == 0 ? (Int32?)null : (Int32?)relatedRecordNumberMaster);
			Int32? recordEntry = (relatedRecordNumberEntry == 0 ? (Int32?)null : (Int32?)relatedRecordNumberEntry);
			return _FillRelationRecord(recordMaster, recordEntry);
		}
		/// <summary>
		/// Do aktuálních dat pro vyhledání vztaženého záznamu vyplní číslo záznamu vztaženého objektu.
		/// Použije se tehdy, když při hledání vztaženého objektu (v paměti) známe jeho číslo, ale objekt samotný nemáme v paměti načten.
		/// Typicky při hledání vztaženého záznamu pro statický vztah známe číslo vztaženého subjektu, protože jej máme uložené v objektu this.OriginData v některé property,
		/// ale navázaný objekt v paměti nemáme. 
		/// Uložíme tedy jeho číslo touto metodou, a z metody TryGetDataRelated() vrátíme false (tj. objekt jsme nenašli).
		/// Následně tuto informaci převezme Evaluator, a postaví SELECT tak, že bude začínat vyhledáním právě tohoto záznamu...
		/// </summary>
		/// <param name="relatedRecordNumberMaster">Číslo záznamu (Master = cislo_subjektu / cislo_nonsubjektu) do vztahu</param>
		/// <param name="relatedRecordNumberEntry">Číslo položky záznamu (Entry = cislo_objektu) do vztahu</param>
		public bool FillRelationRecord(SqlInt32 relatedRecordNumberMaster, SqlInt32 relatedRecordNumberEntry)
		{
			Int32? recordMaster = (relatedRecordNumberMaster.IsNull ? (Int32?)null : (Int32?)relatedRecordNumberMaster.Value);
			Int32? recordEntry = (relatedRecordNumberEntry.IsNull ? (Int32?)null : (Int32?)relatedRecordNumberEntry.Value);
			return _FillRelationRecord(recordMaster, recordEntry);
		}
		/// <summary>
		/// Do aktuálních dat pro vyhledání vztaženého záznamu vyplní číslo záznamu vztaženého objektu.
		/// Použije se tehdy, když při hledání vztaženého objektu (v paměti) známe jeho číslo, ale objekt samotný nemáme v paměti načten.
		/// Typicky při hledání vztaženého záznamu pro statický vztah známe číslo vztaženého subjektu, protože jej máme uložené v objektu this.OriginData v některé property,
		/// ale navázaný objekt v paměti nemáme. 
		/// Uložíme tedy jeho číslo touto metodou, a z metody TryGetDataRelated() vrátíme false (tj. objekt jsme nenašli).
		/// Následně tuto informaci převezme Evaluator, a postaví SELECT tak, že bude začínat vyhledáním právě tohoto záznamu...
		/// </summary>
		/// <param name="relatedRecordNumberMaster">Číslo záznamu (Master = cislo_subjektu / cislo_nonsubjektu) do vztahu</param>
		/// <param name="relatedRecordNumberEntry">Číslo položky záznamu (Entry = cislo_objektu) do vztahu</param>
		public bool FillRelationRecord(Int32? relatedRecordNumberMaster, Int32? relatedRecordNumberEntry)
		{
			return _FillRelationRecord(relatedRecordNumberMaster, relatedRecordNumberEntry);
		}
        /// <summary>
        /// Do aktuálních dat pro vyhledání vztaženého záznamu vyplní číslo záznamu vztaženého objektu.
        /// </summary>
        /// <param name="relatedRecordNumberMaster"></param>
		private bool _FillRelationRecord(Int32? relatedRecordNumberMaster, Int32? relatedRecordNumberEntry)
        {
			if (this.CurrentVariableItem.IsClass)
			{
				this.CurrentVariableItem.InMemoryRecordNumberMaster = relatedRecordNumberMaster;
				this.CurrentVariableItem.InMemoryRecordNumberEntry =  relatedRecordNumberEntry;
			}
            this.RelationVariableItem.InMemoryRecordNumberMaster = relatedRecordNumberMaster;
			this.RelationVariableItem.InMemoryRecordNumberEntry = relatedRecordNumberEntry;
            this.RelationVariableItem.InMemoryFound = relatedRecordNumberMaster.HasValue;
            return relatedRecordNumberMaster.HasValue;
        }
        /// <summary>
        /// Speciální metoda pro uložení předané hodnoty do ResultData, a pro vrácení true.
        /// Zní to divně, ale hodí se to - v metodě: IEvaluationDataSource.TryGetDataObject(args), která má vracet bool.
        /// Běžná konstrukce kódu vede k tomu, že v switch (args.ClassNumber) se rozbočuje dle čísla hledané třídy,
        /// a pak se hledá konkrétní objekt. Ten se má uložit do args.ResultValue a má se vrátit true, pokud se objekt skutečně našel.
        /// Zjednodušení kódu lze dosáhnout tak, že se použije konstrukce:
        /// case 1234: return args.FillResultData(FindObject(123456));       // Do args.ResultData uloží nalezená data, a vrátí true / false podle toho, zda se našel objekt != null.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool FillResultData(object data)
        {
            this.ResultData = data;
            this.CurrentVariableItem.InMemoryDataObject = data;
            this.CurrentVariableItem.InMemoryFound = (data != null);
            if (data != null && data is IRecord)
            {
                int recordNumber = ((IRecord)data).RecordNumber;
                this.CurrentVariableItem.InMemoryRecordNumberMaster = recordNumber;
                this.RelationVariableItem.InMemoryRecordNumberMaster = recordNumber;
                this.RelationVariableItem.InMemoryFound = true;
            }
            return (data != null);
        }
        /// <summary>
        /// Speciální metoda pro uložení předané hodnoty do ResultData, a pro vrácení true.
        /// Zní to divně, ale hodí se to - v metodě: IEvaluationDataSource.TryGetDataObject(args), která má vracet bool.
        /// Běžná konstrukce kódu vede k tomu, že v switch (args.ClassNumber) se rozbočuje dle čísla hledané třídy,
        /// a pak se hledá konkrétní objekt. Ten se má uložit do args.ResultValue a má se vrátit true, pokud se objekt skutečně našel.
        /// Zjednodušení kódu lze dosáhnout tak, že se použije konstrukce:
        /// case 1234: return args.FillResultData(FindObject(123456));       // Do args.ResultData uloží nalezená data, a vrátí true / false podle toho, zda se našel objekt != null.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool FillResultData(object data, Int32? relatedRecordNumber)
        {
            this.ResultData = data;
            this.CurrentVariableItem.InMemoryDataObject = data;
            this.CurrentVariableItem.InMemoryFound = (data != null);
            if (relatedRecordNumber.HasValue)
            {
                this.CurrentVariableItem.InMemoryRecordNumberMaster = relatedRecordNumber;
                this.RelationVariableItem.InMemoryRecordNumberMaster = relatedRecordNumber;
                this.RelationVariableItem.InMemoryFound = true;
            }
            return (data != null);
        }
    }
    /// <summary>
    /// Třída s daty pro vyhledání hodnoty z atributu (columnu) z již nalezeného datového objektu
    /// </summary>
    public class EvaluationDataSourceGetValueArgs : EvaluationDataSourceArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="variable">Celá proměnná (všechny položky)</param>
        /// <param name="index">Index aktuální položky proměnné</param>
        /// <param name="configuredText">Data elementu</param>
        /// <param name="data">Dříve nalezený objekt s daty</param>
        public EvaluationDataSourceGetValueArgs(Expressions.Variable variable, int index, IConfiguredText configuredText, Expressions.VariableItem dataItem)
            : base(variable, index, configuredText)
        {
            this.Data = dataItem.InMemoryDataObject;
            this.DataClassNumber = dataItem.ClassNumber;
        }
        /// <summary>
        /// Zde se nachází objekt s daty, která našla předchozí metoda GetData.
        /// </summary>
        public object Data { get; private set; }
        /// <summary>
        /// Číslo třídy objektu s daty, je odvozeno z čísla třídy té úrovně, kde byl objekt s daty získán.
        /// </summary>
        public int DataClassNumber { get; set; }
        /// <summary>
        /// Název tabulky
        /// </summary>
        public string AttributeTable { get { return this.CurrentVariableItem.AttributeTable; } }
        /// <summary>
        /// Název atributu
        /// </summary>
        public string AttributeName { get { return this.CurrentVariableItem.AttributeName; } }
        /// <summary>
        /// Sem bude umístěna hodnota atributu.
        /// Hodnotu je internal: neumisťuje ji datový zdroj přímo, ale pomocí metody FillResultValue(), 
        /// to aby se dodržela obdobná konvence společná s ostatními typy argumentu.
        /// </summary>
        internal object ResultValue { get; set; }
        /// <summary>
        /// Speciální metoda pro uložení předané hodnoty do ResultValue, a pro vrácení true.
        /// Zní to divně, ale hodí se to - v metodě: IEvaluationDataSource.TryGetValue(args), která má vracet bool.
        /// Běžná konstrukce kódu vede k tomu, že v switch (args.AttributeName) se rozbočuje dle jména atributu,
        /// jeho hodnota se má uložit do args.ResultValue a má se vrátit true.
        /// Zjednodušení kódu lze dosáhnout tak, že se použije konstrukce:
        /// case "Jméno": return args.FillResultValue("Hodnota");       // Do args.ResultValue uloží hodnotu, a vrátí true.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool FillResultValue(object value)
        {
            this.ResultValue = value;
            this.CurrentVariableItem.InMemoryDataValue = value;
            this.CurrentVariableItem.InMemoryFound = true;
            return true;
        }
    }
    /// <summary>
    /// Bázová třída pro hledání dat v datovém zdroji
    /// </summary>
    public class EvaluationDataSourceArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="variable">Celá proměnná (všechny položky)</param>
        /// <param name="index">Index aktuální položky proměnné</param>
        /// <param name="configuredText">Data elementu</param>
        public EvaluationDataSourceArgs(Expressions.Variable variable, int index, IConfiguredText configuredText)
        {
            this.Variable = variable;
            this.CurrentVariableIndex = index;
            this.ElementData = configuredText;
        }
        /// <summary>
        /// Celá proměnná (všechny položky)
        /// </summary>
        public Expressions.Variable Variable { get; private set; }
        /// <summary>
        /// Index aktuální položky proměnné
        /// </summary>
        public int CurrentVariableIndex { get; private set; }
        /// <summary>
        /// Aktuální položka proměnné
        /// </summary>
        public Expressions.VariableItem CurrentVariableItem { get { return this.Variable.Items[this.CurrentVariableIndex]; } }
        /// <summary>
        /// Číslo třídy aktuální položky proměnné (this.CurrentVariableItem.ClassNumber)
        /// </summary>
        public int ClassNumber { get { return this.CurrentVariableItem.ClassNumber; } }
        /// <summary>
        /// Výchozí element, pro který se hledají data
        /// </summary>
        public IConfiguredText ElementData { get; private set; }
        /// <summary>
        /// DataPointer z výchozího elementu (ElementData)
        /// </summary>
        public DataPointerStr ElementPointer { get { return new DataPointerStr(this.ElementData.RowGId, this.ElementData.GId); } }
    }
    #endregion
}
